/*
 * roses.c
 *
 *  Created on: Apr 11, 2011
 *      Author: xozyain
 */
#include "types.h"
//#include "stat.h"
#include "user.h"
#include "fcntl.h"
#include "boundedbuffer.h"
#include "roses.h"

#define STK_SZ 512
#define LOG_FILE_NAME "ass2_log.txt"
#define CONF_FILE_NAME "con.conf"
#define MAX_LINE_SIZE 32


struct BB * rbb; // requests bounded buffer
int rbb_binsem_id; // requests BB mutex

struct BB * cbb; // cups bounded buffer
int cbb_binsem_id; //used by the busboy to go to sleep, and wake up
int washing_mutex;

int gfd; // our global file descriptor for the log file (ass2_log.txt)

int nextRequestId = 0; // a running index of a request id
int binsemReqId; // mutex for nextRequestId


int retVal; // dummy for return value
int successVal = 0;
int failVal = -1;

int * hresult; // a dummy pointer for the return values of the threads. we don't read from it.

rTotal * hostessTotal; //arrays of thread ids for the hostesses.
rTotal * bartendersTotal; //arrays of thread ids for the bartenders.

int bartenderDone=0; // a variable indicates if the bartenders finished all the request.

//struct semaphore * allRequestsForBartenders;



// for hostess & bartender tracking
// the trick is: im badakta ereh, gam ithayavta elav
int requestsLeftFromQuote(rTotal * rt){
	int ans;
	if (binary_sem_down(rt->binSemId)<0){
		printf(2,"requestsLeft: binary_sem_down error\n");
		thread_exit(&failVal);
	}
	ans = rt->quota - rt->done;
	if (ans < 0)
		ans=0;
	else
		rt->done++;
	if (binary_sem_up(rt->binSemId)<0){
		printf(2,"requestsLeft: binary_sem_up error\n");
		thread_exit(&failVal);
	}
	return ans;
}

void addNewRequest( struct Request* newReq){

	semaphore_put(rbb,(void *)newReq);

}

rqst * getRequest(){
	return ((rqst*)semaphore_pop(rbb));
}

cup * getCleanCup(){
	cup * retcup;
	uint percentage;

	// lock down cbb->count
	if (binary_sem_down(washing_mutex)<0){
		printf(2,"getCleanCup: binary_sem_down: error\n");
		thread_exit(&failVal);
	}
	percentage = (cbb->count*100) / cbb->_max_capacity;
	if ( percentage<= 15 || (!percentage)){
		if (binary_sem_up(cbb_binsem_id)<0){ // wake the busboy
			printf(2,"getCleanCup: binary_sem_up: error\n");
			thread_exit(&failVal);
		}
	}
	retcup = (cup*)semaphore_pop(cbb);

	//release cbb->count
	if (binary_sem_up(washing_mutex)<0){
		printf(2,"getCleanCup: binary_sem_up: error\n");
		thread_exit(&failVal);
	}

	return retcup;
}

int washCups(){
	int cleanCups;
	cup * washedcup;
	int washcount;

	washcount = 0;
	cleanCups = cbb->count;
	while (((cleanCups * 100)/cbb->_max_capacity) < 85){
		if (!(washedcup = (cup *)malloc(sizeof(cup))))
			return -1;
		washedcup->clean = 1;
		semaphore_put(cbb, (void*)washedcup); // add washed cup to Cups BB
		washcount++;
		cleanCups = cbb->count;
	}
	printf(gfd,"Busboy %d added %d clean cups.\n",thread_getid(),washcount);
	printf(1,"Busboy %d added %d clean cups.\n",thread_getid(),washcount);
	return 0;
}

void * hostessRun(void){

	rqst * tmprequest;

	while (requestsLeftFromQuote(hostessTotal)){
		if ((tmprequest = (rqst *)malloc(sizeof(rqst))) == 0){
			printf(2,"MALLOC ERROR: hostess\n. terminating...\n");
			thread_exit((void *)&retVal);
		}
		tmprequest->hostessOwner = thread_getid();
		if (binary_sem_down(binsemReqId)<0){
			printf(2,"hostessrun: binary_sem_down: error\n");
			thread_exit(&failVal);
		}

		tmprequest->rid = nextRequestId++;
		if (binary_sem_up(binsemReqId)<0){
			printf(2,"hostessrun: binary_sem_up: error\n");
			thread_exit(&failVal);
		}
		addNewRequest(tmprequest);

		//TODO: add a binary semaphore to sync the printf's to the log file
		printf(gfd,"Hostess %d added a new request #%d\n", thread_getid(), tmprequest->rid);
		printf(1,"Hostess %d added a new request #%d\n", thread_getid(), tmprequest->rid);

		sleep(10);
	}

	thread_exit(&successVal);
	return 0;
}


void * bartenderRun(void){
	rqst * tmpreq;
	cup * tmpcup;
	int mytid;


	mytid = thread_getid();
	while(requestsLeftFromQuote(bartendersTotal)){

		tmpreq = getRequest();
		tmpcup = getCleanCup();
		printf(gfd, "Bartender %d completed request #%d\n",mytid, tmpreq->rid);
		printf(1, "Bartender %d completed request #%d\n",mytid, tmpreq->rid);

		tmpcup->clean = 0;

		free(tmpreq);
		free(tmpcup);
		sleep(10);
	}
	thread_exit(&failVal);
	return 0;
}


void * busboyRun(void){

	if (binary_sem_down(cbb_binsem_id)<0){ // set cbb_binsem_id=0 (was =1)
		printf(2,"busboy: failed to start\n");
		thread_exit(&failVal);
	}
	if (binary_sem_down(cbb_binsem_id)<0){ // going to sleep, until cups need to be washed
		printf(2,"busboy: failed to start\n");
		thread_exit(&failVal);
	}

	while(!(bartenderDone)){
		// wake up here...
		if (washCups()<0){
			printf(2,"busboy: washCups: error\n");
			break;
		}
		// go back to sleep
		if (binary_sem_down(cbb_binsem_id)<0){
			printf(2,"busboy: binary_sem_down: error\n");
			break;
		}
	}
	thread_exit(&failVal);
	return 0;
}


// Fill in first C cups
void initCups(int C){
	int i;
	cup * newcup;


	for (i = 0; i < C; ++i) {

		if (!(newcup = (cup*)malloc(sizeof(cup)))){
			printf(2,"initCups: malloc error\n");
			break;
		}

		newcup->clean = 1;
		semaphore_put(cbb, (void*)newcup);
	}
}

int main(int argc, char *argv[])
{

	int fd_conf;
	char * nextLine;
	int B,H,R,C,totalRequests;

	int * hostessThreads;
	int busboyThread;
	int * bartenderThreads;
	int j;
	void * stacktmp;

	B=H=R=C=totalRequests=0;



	if (!(hostessTotal = (rTotal*)malloc(sizeof(rTotal)))){
		printf(2, "main: malloc error\n");
		exit();
	}
	if (!(bartendersTotal = (rTotal*)malloc(sizeof(rTotal)))){
		printf(2, "main: malloc error\n");
		exit();
	}
	if ((gfd = open(LOG_FILE_NAME, O_CREATE | O_WRONLY))<0){
		printf(2,"ass2_log.txt failed to open");
	}

	if ((fd_conf = open(CONF_FILE_NAME, O_RDONLY))<0){
		printf(2,"con.conf failed to open");
	}
	// TODO: read from con.conf... B,H,R,C,totalRequests...
	if (!(nextLine = (char*)malloc(MAX_LINE_SIZE))){
		printf(2, "main: malloc error\n");
		exit();
	}
	// ASSUMES SPECIFIC FORMAT FOR con.conf!!!!!!!!
	if (*getsfd(nextLine,MAX_LINE_SIZE, fd_conf) != '\0'){
		B = atoi(nextLine+2);
	}

	if (*getsfd(nextLine,MAX_LINE_SIZE, fd_conf) != '\0'){
		H = atoi(nextLine+2);
	}

	if (*getsfd(nextLine,MAX_LINE_SIZE, fd_conf) != '\0'){
		R = atoi(nextLine+2);
	}

	if (*getsfd(nextLine,MAX_LINE_SIZE, fd_conf) != '\0'){
		C = atoi(nextLine+2);
	}

	if (*getsfd(nextLine,MAX_LINE_SIZE, fd_conf) != '\0'){
		totalRequests = atoi(nextLine+14);
	}

	if (close(fd_conf)<0)
		printf(2,"close(fd_conf) error!\n");

	if (!(hostessThreads = (int*)malloc(sizeof(int)*H))){
		printf(2, "main: malloc error\n");
		exit();
	}
	if (!(bartenderThreads = (int*)malloc(sizeof(int)*B))){
		printf(2, "main: malloc error\n");
		exit();
	}

	hostessTotal->quota = totalRequests;
	hostessTotal->done = 0;
	hostessTotal->binSemId = binary_sem_create();

	bartendersTotal->quota = totalRequests;
	bartendersTotal->done = 0;
	bartendersTotal->binSemId = binary_sem_create();


	cbb_binsem_id = binary_sem_create();
	rbb_binsem_id = binary_sem_create();
	washing_mutex = binary_sem_create();

	binsemReqId = binary_sem_create();

	rbb = BB_create(R);
	cbb = BB_create(C);


	//Fill in first C cups
	initCups(C);



	// Creating hostesses
	for (j = 0; j < H; ++j) {
		if (!(stacktmp = malloc(STK_SZ))){
			printf(2, "main: malloc ERROR!\n");
			exit();
		}
		hostessThreads[j] = thread_create(&hostessRun,stacktmp, STK_SZ);
	}

	//Launching the bus boy
	if (!(stacktmp = malloc(STK_SZ))){
		printf(2, "main: malloc ERROR!\n");
		exit();
	}
	busboyThread = thread_create(&busboyRun,stacktmp, STK_SZ);

	// Creating bartenders
	for (j = 0; j < B; ++j) {
		if (!(stacktmp = malloc(STK_SZ))){
			printf(2, "main: malloc ERROR!\n");
			exit();
		}
		bartenderThreads[j] = thread_create(&bartenderRun,stacktmp, STK_SZ);
	}



	if (!(hresult = (int*)malloc(sizeof(int)))){
		printf(2,"main: malloc ERROR!\n");
		exit();
	}

	// First join on the hostesses. they are done first
	for (j = 0; j < H; ++j) {
		thread_join(hostessThreads[j], (void**)&hresult);
	}

	// Then the bartenders
	for (j = 0; j < B; ++j) {
		thread_join(bartenderThreads[j], (void**)&hresult);
	}

	// a flag for the busboy
	bartenderDone=1;

	binary_sem_up(cbb_binsem_id); // Wake up our sleeping boy
	thread_join(busboyThread, (void**)&hresult);



	if (close(gfd)<0)
		printf(2,"close(gfd) error!\n");
	thread_exit(&successVal);
	return 0;

}
